package io.xxx.omni.sync.impl

import com.alibaba.fastjson.JSONObject
import io.xxx.omni.sync.Document
import io.xxx.omni.sync.PagePorter
import io.xxx.omni.sync.Store
import io.xxx.omni.sync.StoreJob
import io.xxx.omni.util.dateTimeFormatter
import io.xxx.omni.util.offset
import io.xxx.omni.util.toLocalDateTime
import org.apache.commons.codec.digest.DigestUtils
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.util.*

/**
 * 贝贝
 */
abstract class BbPorter : PagePorter() {

    private val httpUrl = "http://api.open.beibei.com/outer_api/out_gateway/route.html"

    override val platformCode = "beibei"

    protected abstract val method: String

    protected fun getResponse(
        store: Store,
        params: Map<String, String> = emptyMap(),
        checked: Boolean = true
    ): JSONObject {
        val signMap = TreeMap(params)
        signMap["method"] = method
        signMap["timestamp"] = LocalDateTime.now().toEpochSecond(offset).toString()
        signMap["app_id"] = store.appKey!!
        signMap["session"] = store.accessToken!!
        val builder = StringBuilder().append(store.appSecret)
        signMap.forEach { (k, v) -> builder.append(k).append(v) }
        builder.append(store.appSecret)
        val sign = DigestUtils.md5Hex(builder.toString()).toUpperCase()
        signMap["sign"] = sign
        val response = restTemplate.postForEntity(httpUrl, signMap, JSONObject::class.java)
        if (response.statusCode.isError || response.body == null) {
            val msg = "接口[method: $method, queryParams: $signMap]异常"
            throw RuntimeException(msg)
        }
        val body = response.body!!
        if (checked && !body.getBoolean("success")) {
            val msg = "接口[method: $method, queryParams: $signMap]异常: $body"
            log.error(msg)
            throwException(method, null, body.getString("message"))
        }
        return response.body!!
    }

    protected fun getParams(
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        timeRange: String,
        pageNo: Long = 1
    ): Map<String, String> {
        return mapOf(
            "time_range" to timeRange,
            "start_time" to startTime.format(dateTimeFormatter),
            "end_time" to endTime.format(dateTimeFormatter),
            "page_no" to pageNo.toString(),
            "page_size" to pageSize.toString(),
        )
    }

    companion object {
        private val log = LoggerFactory.getLogger(BbPorter::class.java)
    }
}

@Component
class BbTradePorter : BbPorter() {

    override val method = "beibei.outer.trade.order.get"

    override val jobId = 1

    override fun getParameters(store: Store): List<Any> {
        return listOf("modified_time")
    }

    override fun getStartTime(storeJob: StoreJob): LocalDateTime {
        val minStartTime = LocalDateTime.now().minusDays(30).plusMinutes(1)
        return if (storeJob.endTime!! < minStartTime) minStartTime else storeJob.endTime!!
    }

    override fun getCount(store: Store, startTime: LocalDateTime, endTime: LocalDateTime, parameter: Any?): Long? {
        val params = getParams(startTime, endTime, parameter as String)
        val response = getResponse(store, params)
        return response.getLong("count")
    }

    override fun getData(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?,
        pageNo: Long,
    ): List<Document>? {
        val params = getParams(startTime, endTime, parameter as String, pageNo)
        val response = getResponse(store, params)
        return response.getJSONArray("data")?.map {
            @Suppress("unchecked_cast")
            val value = JSONObject(it as Map<String, Any>)
            Document(
                value.getString("oid"),
                value,
                value.getString("modified_time").toLocalDateTime()
            )
        }
    }
}

@Component
class BbTradeProcurator : BbTradePorter() {

    override val jobId = 3

    override val cron = "0/10 * * * * ?"

    override fun getParameters(store: Store): List<Any> {
        return listOf("create_time")
    }
}

@Component
class BbRefundPorter : BbPorter() {

    override val method = "beibei.outer.refunds.get"

    override val jobId = 2

    override fun getParameters(store: Store): List<Any> {
        return listOf("modified_time")
    }

    override fun getCount(store: Store, startTime: LocalDateTime, endTime: LocalDateTime, parameter: Any?): Long? {
        val params = getParams(startTime, endTime, parameter as String)
        val response = getResponse(store, params, false)
        return response.getLong("count")
    }

    override fun getData(
        store: Store,
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        parameter: Any?,
        pageNo: Long,
    ): List<Document>? {
        val params = getParams(startTime, endTime, parameter as String, pageNo)
        val response = getResponse(store, params, false)
        return response.getJSONArray("data")?.map {
            @Suppress("unchecked_cast")
            val value = JSONObject(it as Map<String, Any>)
            Document(
                value.getString("id"),
                value.getString("oid"),
                value,
                value.getString("modified_time").toLocalDateTime()
            )
        }
    }
}

@Component
class BbRefundProcurator : BbTradePorter() {

    override val jobId = 4

    override val cron = "0/10 * * * * ?"

    override fun getParameters(store: Store): List<Any> {
        return listOf("create_time")
    }
}